package com.atguigu.jinx.service.impl;


import cn.hutool.core.util.IdUtil;
import com.alipay.api.AlipayApiException;
import com.atguigu.jinx.conf.RabbitMQConfig;
import com.atguigu.jinx.conf.WxPayConfig;
import com.atguigu.jinx.entity.dto.*;
import com.atguigu.jinx.entity.vo.IncomeAndExpenditureSituationVO;
import com.atguigu.jinx.entity.vo.PayVo;
import com.atguigu.jinx.entity.vo.UserDisplayBalanceIneStatisticsVO;
import com.atguigu.jinx.enums.PayMethodStatus;
import com.atguigu.jinx.enums.PayType;
import com.atguigu.jinx.enums.ProcessStatus;
import com.atguigu.jinx.enums.RecordStatus;
import com.atguigu.jinx.enums.wxpay.NativeOrderData;
import com.atguigu.jinx.enums.wxpay.WxApiType;
import com.atguigu.jinx.enums.wxpay.WxNotifyType;
import com.atguigu.jinx.exception.BusinessException;
import com.atguigu.jinx.mapper.*;
import com.atguigu.jinx.service.IRechargeRequestService;
import com.atguigu.jinx.service.IUserBalanceService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import static com.atguigu.jinx.conf.RabbitMQConfig.DEAD_LETTER_ROUTING_KEY;
import static com.atguigu.jinx.enums.JobStatus.COMPLETED;
import static com.atguigu.jinx.utils.NameConstants.ALIPAY_BODY_CHARGE_PATTERN;
import static com.atguigu.jinx.utils.NameConstants.USER_CHARGE_AMOUNT;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author steve
 * @since 2025-08-27
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserBalanceServiceImpl extends ServiceImpl<UserBalanceMapper, UserBalance> implements IUserBalanceService {

    private final UserMapper userMapper;

    private final UserBalanceMapper userBalanceMapper;

    private final UserJobOrderMapper userJobOrderMapper;

    private final WithdrawRequestMapper withdrawRequestMapper;

    private final RechargeRequestMapper rechargeRequestMapper;

    private final AlipayTemplate alipayTemplate;

    private final RabbitTemplate rabbitTemplate;

    private final StringRedisTemplate redisTemplate;

    private final WxPayConfig wxPayConfig;

    private final Gson gson;

    private final IRechargeRequestService rechargeRequestService;

    private final CloseableHttpClient httpClient;


    @Autowired
    @Qualifier("ioTaskExecutor")
    private Executor ioTaskExecutor;

    /**
     * 用户展示余额收支统计信息
     */
    @Override
    @SneakyThrows
    public UserDisplayBalanceIneStatisticsVO userDisplayBalanceIneStatistics(String startMonth, String endMonth) {

        Long id = getUserId();

        // 获取当月起始时间
        LocalDateTime startOfMonth = LocalDate.now().withDayOfMonth(1).atStartOfDay();
        // 获取上月起始时间
        LocalDateTime startOfLastMonth = startOfMonth.minusMonths(1);
        // 获取下月起始时间
        LocalDateTime startOfNextMonth = startOfMonth.plusMonths(1);
        // 1.查询用户的余额
        UserBalance userBalance = userBalanceMapper.selectOne(new LambdaQueryWrapper<UserBalance>().eq(UserBalance::getUserId, id));

        Assert.notNull(userBalance, "查询钱包中出现问题,请联系管理员");

        CompletableFuture<BigDecimal> thisMonthIncomeFuture =
                CompletableFuture.supplyAsync(() -> getIncomeMonthly(id, startOfMonth, startOfNextMonth),ioTaskExecutor);

        CompletableFuture<BigDecimal> lastMonthIncomeFuture =
                CompletableFuture.supplyAsync(() -> getIncomeMonthly(id, startOfLastMonth, startOfMonth),ioTaskExecutor);

        CompletableFuture<BigDecimal> thisMonthExpenditureFuture =
                CompletableFuture.supplyAsync(() -> getExpenditureMonthly(id, startOfMonth, startOfNextMonth),ioTaskExecutor);

        CompletableFuture<BigDecimal> lastMonthExpenditureFuture =
                CompletableFuture.supplyAsync(() -> getExpenditureMonthly(id, startOfLastMonth, startOfMonth),ioTaskExecutor);

        // 等待所有任务完成
        CompletableFuture.allOf(thisMonthIncomeFuture,
                lastMonthIncomeFuture,
                thisMonthExpenditureFuture,
                lastMonthExpenditureFuture).join();

        // 获取结果
        BigDecimal thisMonthIncomeSum = thisMonthIncomeFuture.get();
        BigDecimal lastMonthIncomeSum = lastMonthIncomeFuture.get();
        BigDecimal thisMonthExpenditureSum = thisMonthExpenditureFuture.get();
        BigDecimal lastMonthExpenditureSum = lastMonthExpenditureFuture.get();

        // 封装结果
        UserDisplayBalanceIneStatisticsVO vo = new UserDisplayBalanceIneStatisticsVO();
        /// 余额
        vo.setBalance(userBalance.getBalance());
        /// 当月收入
        vo.setMonthlyIncome(thisMonthIncomeSum);
        /// 和上个月收入比
        vo.setComparedWithLastMonthIncome(calcGrowthRate(thisMonthIncomeSum, lastMonthIncomeSum));
        /// 当月提现
        vo.setMonthlyExpenditure(thisMonthExpenditureSum);
        /// 和上个月提现比
        vo.setComparedWithLastMonthExpenditure(calcGrowthRate(thisMonthExpenditureSum, lastMonthExpenditureSum));
        /// 用户的月份额度表
        vo.setListIncomeWithdrawalStatistics(getIncomeExpenditureMonthly(id, startMonth, endMonth));

        return vo;
    }


    /**
     * 获取用户的额度统计单
     *
     * @param id
     * @param startMonth
     * @param endMonth
     * @return
     */
    private List<UserDisplayBalanceIneStatisticsVO.IncomeWithdrawalStatisticsData> getIncomeExpenditureMonthly(Long id, String startMonth, String endMonth) {
        // 转成 YearMonth
        String start = YearMonth.parse(startMonth).atDay(1).toString();
        String end = YearMonth.parse(endMonth).atDay(1).toString();
        return withdrawRequestMapper.selectByMonthGroup(id, startMonth, start, end);
    }


    private BigDecimal getExpenditureMonthly(Long id, LocalDateTime startOfLastMonth, LocalDateTime startOfMonth) {
        LambdaQueryWrapper<WithdrawRequest> withWrapper = new LambdaQueryWrapper<>();
        withWrapper.eq(WithdrawRequest::getUserId, id)
                // 这里不是结单 而是支付宝微信的提现状态为2-成功
                .eq(WithdrawRequest::getStatus, COMPLETED)
                .ge(WithdrawRequest::getWithdrawDatetime, startOfLastMonth)
                .le(WithdrawRequest::getWithdrawDatetime, startOfMonth);
        List<WithdrawRequest> monthWithdraw = withdrawRequestMapper.selectList(withWrapper);
        return monthWithdraw.stream()
                .map(u -> u.getAmount() == null ? BigDecimal.ZERO : u.getAmount())
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getIncomeMonthly(Long id, LocalDateTime startOfMonth, LocalDateTime startOfNextMonth) {
        LambdaQueryWrapper<UserJobOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserJobOrder::getUserId, id)
                .eq(UserJobOrder::getStatus, COMPLETED.getCode())
                .ge(UserJobOrder::getCompleteDatetime, startOfMonth)
                .le(UserJobOrder::getCompleteDatetime, startOfNextMonth);
        List<UserJobOrder> monthOrders = userJobOrderMapper.selectList(wrapper);
        return monthOrders.stream()
                .map(u -> u.getTotalBonus() == null ? BigDecimal.ZERO : u.getTotalBonus())
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }


    @Override
    public List<IncomeAndExpenditureSituationVO> userDisplayIneListInfo() {
        Long userId = getUserId();
        // 充值订单记录表查询
        CompletableFuture<List<IncomeAndExpenditureSituationVO>> rechargeFuture =
                CompletableFuture.supplyAsync(() -> {
                    LambdaQueryWrapper<RechargeRequest> rechargeWrapper = new LambdaQueryWrapper<>();
                    rechargeWrapper.eq(RechargeRequest::getUserId, userId);
                    List<RechargeRequest> rechargeRequests = rechargeRequestMapper.selectList(rechargeWrapper);
                    return rechargeRequests.stream().map(rechargeRequest -> {
                        IncomeAndExpenditureSituationVO vo = new IncomeAndExpenditureSituationVO();
                        // 支付时间
                        /// 这里因为paid_at 是在支付收到回调成功之后才出现,所以采用下单时刻
                        vo.setDatetime(rechargeRequest.getCreatedAt());
                        vo.setType(RecordStatus.RECHARGE.getName());
                        vo.setStatus(ProcessStatus.getNameByCode(rechargeRequest.getStatus()));
                        vo.setMoney(rechargeRequest.getAmount() != null ? rechargeRequest.getAmount() : BigDecimal.ZERO);
                        vo.setPayMethod(PayMethodStatus.getNameByCode(rechargeRequest.getPayMethod()));
                        return vo;
                    }).toList();
                }, ioTaskExecutor);

        // 收入订单记录表查询
        CompletableFuture<List<IncomeAndExpenditureSituationVO>> incomeFuture =
                CompletableFuture.supplyAsync(() -> {
                    LambdaQueryWrapper<UserJobOrder> userJobWrapper = new LambdaQueryWrapper<>();
                    userJobWrapper.eq(UserJobOrder::getUserId, userId);
                    List<UserJobOrder> userJobOrders = userJobOrderMapper.selectList(userJobWrapper);
                    return userJobOrders.stream().map(job -> {
                        IncomeAndExpenditureSituationVO vo2 = new IncomeAndExpenditureSituationVO();
                        vo2.setDatetime(job.getCompleteDatetime());
                        vo2.setType(RecordStatus.INCOME.getName());
                        vo2.setStatus(ProcessStatus.getNameByCode(job.getStatus()));
                        vo2.setMoney(job.getTotalBonus() != null ? job.getTotalBonus() : BigDecimal.ZERO);
                        return vo2;
                    }).toList();
                }, ioTaskExecutor);

        // 提现订单记录表查询
        CompletableFuture<List<IncomeAndExpenditureSituationVO>> withdrawFuture =
                CompletableFuture.supplyAsync(() -> {
                    LambdaQueryWrapper<WithdrawRequest> withdrawWrapper = new LambdaQueryWrapper<>();
                    withdrawWrapper.eq(WithdrawRequest::getUserId, userId);
                    List<WithdrawRequest> withdrawRequests = withdrawRequestMapper.selectList(withdrawWrapper);
                    return withdrawRequests.stream().map(req -> {
                        IncomeAndExpenditureSituationVO vo3 = new IncomeAndExpenditureSituationVO();
                        vo3.setDatetime(req.getWithdrawDatetime());
                        vo3.setType(RecordStatus.WITHDRAW.getName());
                        vo3.setStatus(ProcessStatus.getNameByCode(req.getStatus()));
                        vo3.setMoney(req.getAmount() != null ? req.getAmount() : BigDecimal.ZERO);
                        vo3.setPayMethod(PayMethodStatus.getNameByCode(req.getMethod()));
                        return vo3;
                    }).toList();
                }, ioTaskExecutor);

        // 等待全部完成
        CompletableFuture<Void> allOf = CompletableFuture.allOf(rechargeFuture, incomeFuture, withdrawFuture);
        allOf.join();

        // 收集结果
        List<IncomeAndExpenditureSituationVO> list = new ArrayList<>();
        list.addAll(rechargeFuture.join());
        list.addAll(incomeFuture.join());
        list.addAll(withdrawFuture.join());

        // 排序（时间倒序）
        return list.stream()
                .sorted(Comparator.comparing(IncomeAndExpenditureSituationVO::getDatetime).reversed())
                .toList();
    }

    private Long getUserId() {
        String userName = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, userName));
        Assert.notNull(user, "未找到该用户");
        return user.getId();
    }

    /**
     * 支付宝充值
     *
     * @param amount 用户充值金额
     * @return 外链
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String alipayCharge(BigDecimal amount) throws AlipayApiException {
        // 创建订单
        RechargeRequest rechargeRequest = generateRechargeBean(amount);
        // 1.发送支付宝请求
        PayVo payVo = new PayVo(rechargeRequest.getOutOrderNo(),
                USER_CHARGE_AMOUNT.concat(amount.toString()),
                amount.toString(), ALIPAY_BODY_CHARGE_PATTERN);
        String payResultUrl = alipayTemplate.pay(payVo);

        // 确保返回正确的结果
        if (StringUtils.isNotBlank(payResultUrl)) {
            //2. 生成下单对象并存放到本地
            Assert.isTrue(rechargeRequestMapper.insert(rechargeRequest) == 1, "插入失败");

            // 3.处理中的订单存放到TTL队列中,路由到DEAD_CONSUMER查单关单
            // 1.exchange 2.routingkey 3.message
            ///  流程 首先这个rouutineky =test.dead.heHe的数据 会跑到normal交换机当中 => link方法绑定了normal_exchange 和 normal_queue 通过test.#绑定
            ///  normal_queue 设置为无人监听，那么我们在上面声明这个normal_queue的时候给定了一个存活时间为十分钟,并且配置了一个相应的死信交换机=>
            ///  dead_exchange_name 通过 test.dead.heHe绑定,那么我们发送的这条消息的通配符正好是这个test.dead.heHe 接着这个死信交换机绑定了
            ///  一个死信队列，通过通配符 test.dead.heHe 绑定，那么我们的消息会流转到 该死信队列里面,我们只需要监听这个死信队列,然后去查单API拿到获取订单状态
            ///  如果还是没有发生改变,则取消该订单
            rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, DEAD_LETTER_ROUTING_KEY, rechargeRequest.getOrderNo());
        }
        return payResultUrl;
    }


    /**
     * 生成对象
     *
     * @param amount
     * @return
     */
    private RechargeRequest generateRechargeBean(BigDecimal amount) {
        RechargeRequest rechargeRequest = new RechargeRequest();
        rechargeRequest.setAmount(amount);
        rechargeRequest.setUserId(getUserId());
        rechargeRequest.setStatus(ProcessStatus.PROCESSING.getCode());
        rechargeRequest.setPayMethod(PayType.ALIPAY.getCode());
        rechargeRequest.setOrderNo(IdUtil.getSnowflakeNextIdStr());
        rechargeRequest.setOutOrderNo(IdUtil.getSnowflakeNextIdStr());
        return rechargeRequest;
    }

    /**
     * 微信支付native下单
     * TODO 是否需要生成支付实例(调用验收环境后)
     * TODO 生成二维码实例为两个小时
     *
     * @param amount 充值请求参数
     * @return 下单结果
     */
    @Override
//    @SneakyThrows(value = IOException.class)
    public Map<String, Object> wechatpayCharge(BigDecimal amount) {
        // 查询redis 如果存在该金额的url则直接返回

        // 构建下单请求参数
        RechargeRequest rechargeRequest = generateRechargeBean(amount);

        // 返回结果
        /*
            思路：微信支付生成的订单号使用redis进行存储,如果redis中存在二维码实例(amount->out_order_no->code_url)(Hash),则挂载生成本地生成实例订单
            否则进行http链接获取,并绑定本地实例订单
         */
        NativeOrderData nativeOrderData = generateUrlWithOrderNo(amount);

        return (Map<String, Object>) nativeOrderData;


    }

    /**
     * 生成url和订单编号
     *
     * @param amount 订单额度
     * @return 包含微信支付订单号和订单url_code的键值对
     */
    private NativeOrderData generateUrlWithOrderNo(BigDecimal amount) {

        boolean ifExist = Boolean.TRUE.equals(redisTemplate.hasKey(amount.toString()));
        // 存在则直接返回
        if (ifExist) {
            String jsonResult = redisTemplate.opsForValue().get(amount);
            return gson.fromJson(jsonResult, NativeOrderData.class);
        }
        // 调用http请求 向微信发送生成请求,并且存储到redis当中
        return callWechatNativePayToGenerateAnOrder(amount);
    }

    /**
     * 调用微信native支付返回订单结果
     *
     * @param amount 订单的额度
     * @return 生成的结果
     */
    private NativeOrderData callWechatNativePayToGenerateAnOrder(BigDecimal amount) {


        // fixme
        RechargeRequest rechargeRequest = new RechargeRequest();
        String url = wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType());

        HttpPost httpPost = new HttpPost(url);

        // 生成订单状态
        LinkedHashMap<Object, Object> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("appid", wxPayConfig.getAppid());
        linkedHashMap.put("mchid", wxPayConfig.getMchId());
        linkedHashMap.put("description", USER_CHARGE_AMOUNT.concat(amount.toString()));
        linkedHashMap.put("out_trade_no", rechargeRequest.getOrderNo());
        linkedHashMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType()));

        HashMap<Object, Object> amountMap = new HashMap<>(2);
        // 这里必须是整数，单位分
        amountMap.put("total", amount);
        amountMap.put("currency", "CNY");
        linkedHashMap.put("amount", amountMap);

        String jsonParams = gson.toJson(linkedHashMap);

        log.info("微信支付下单请求参数, orderNo={}, params={}", rechargeRequest.getOrderNo(), jsonParams);
        StringEntity entity = new StringEntity(jsonParams, "utf8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {

            //返回状态码
            int statusCode = response.getStatusLine().getStatusCode();
            //
            String responseEntity = EntityUtils.toString(response.getEntity());

            log.info("微信支付响应: orderNo={}, status={}, body={}", rechargeRequest.getOrderNo(), statusCode, responseEntity);

            if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) {
                throw new BusinessException("微信支付下单失败, 状态码=" + statusCode + ", body=" + responseEntity);
            }

            //相应结果
            Map<String, String> resultMap = gson.fromJson(responseEntity, HashMap.class);
            //解析二维码
            String codeUrl = resultMap.get("code_url");

            // 存储该二维码,设置有效期限为两个小时
            redisTemplate.opsForValue().set(amount.toString(), codeUrl, 110, TimeUnit.MINUTES);
            // 返回该响应值
            Map<String, Object> returnMap = new HashMap<>(2);
            returnMap.put("codeUrl", codeUrl);
            returnMap.put("orderNo", rechargeRequest.getOrderNo());
            return new NativeOrderData();
//            return returnMap;
        } catch (ClientProtocolException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 查询订单
     *
     * @param orderNo 订单编号
     * @return 执行查询请求结果
     */
    @Override
    public boolean rechargeOrderRequest(String orderNo) {
        // 查询该笔订单
        LambdaQueryWrapper<RechargeRequest> rechargeWrapper = new LambdaQueryWrapper<>();
        rechargeWrapper.eq(RechargeRequest::getOrderNo, orderNo);
        RechargeRequest rechargeRequest = rechargeRequestMapper.selectOne(rechargeWrapper);
        switch (rechargeRequest.getPayMethod()) {
            // alipay
            case 1 -> {
                // 调用alipay的查单功能
                rechargeRequestService.changeRechargeOrderSyncStatusFromAlipay(orderNo);
            }
            // wechatpay
            case 2 -> {
                // 调用wechatpay的查单功能

            }

        }

        return false;
    }

    /**
     * 计算本月与上月的正负盈余百分比
     *
     * @param thisMonth 本月收入
     * @param lastMonth 上月收入
     * @return 增长率字符串，例如 "+3.9%" 或 "-3.3%"
     */
    public static String calcGrowthRate(BigDecimal thisMonth, BigDecimal lastMonth) {

        if (lastMonth == null && thisMonth == null) {
            return "+0%";
        }
        if (thisMonth.compareTo(BigDecimal.ZERO) == 0 && lastMonth.compareTo(BigDecimal.ZERO) == 0) {
            return "+0%";
        }

        if (lastMonth == null || lastMonth.compareTo(BigDecimal.ZERO) == 0) {
            return "+100%";
        }

        if (thisMonth == null) {
            thisMonth = BigDecimal.ZERO;
        }

        // 增长率 = (本月 - 上月) / 上月
        BigDecimal growth = thisMonth.subtract(lastMonth)
                .divide(lastMonth, 4, RoundingMode.HALF_UP)  // 保留4位小数方便计算
                .multiply(BigDecimal.valueOf(100));          // 转成百分比

        // 保留1位小数
        growth = growth.setScale(1, RoundingMode.HALF_UP);

        // 拼接符号
        String sign = growth.compareTo(BigDecimal.ZERO) >= 0 ? "+" : "";
        return sign + growth.toPlainString() + "%";
    }
}
